/*
 * Funny Language - a free style programming language.
 * Copyright (C) 2015 by fanguangping (fanguangping@163.com)
 * dfa.c
 */

#include <stdio.h>
#include <stdlib.h>
#include "dfa.h"
#include "../base/cmemory.h"

static HashTable stateTable = NULL;

TransitNode *alloc_transit_node() {
    return (TransitNode *) sys_malloc(sizeof(TransitNode));
}

static TransitNode *create_transit(State *starter, State *current, State *next, Statement *statement) {
    TransitNode *node = alloc_transit_node();
    node->starter = starter;
    node->input = statement;
    node->next = next;
    put_table_element(stateTable, current, node, hash_code_state);
    return node;
}

void set_repeat_transition(State *starter, State *repeatState, Statement *repeat) {
    QueueNode *node = NULL;
    State *current = repeatState;

    if (stateTable == NULL) {
        stateTable = init_table();
    }

    if (repeat->children != NULL) {
        node = repeat->children->head;
        while (node->next != repeat->children->tail) {
            State *next = new_state();
            create_transit(starter, current, next, (Statement *) node->element);
            current = next;
            node = node->next;
        }
        create_transit(starter, current, repeatState, (Statement *) node->element);
    }
}

State *to_dfa(Statement *s) {
    if (s == NULL)
        return NULL;
    State *current = new_state();
    State *starter = current;
    QueueNode *node = NULL;
    Statement *st = NULL;
    TransitNode *transitNode = NULL;

    if (stateTable == NULL) {
        stateTable = init_table();
    }

    if (s->children != NULL) {
        node = s->children->head;
        while (node != NULL) {
            st = (Statement *) node->element;
            switch (st->type) {
                case SYM:
                case ARG:
                    transitNode = create_transit(starter, current, new_state(), st);
                    current = transitNode->next;
                    break;

                case LOOP:
                case OPTIONAL:
                    set_repeat_transition(starter, current, st);
                    break;

                default:
                    fprintf(stderr, "to dfa error!\n");
                    break;
            }
            node = node->next;
        }
        current->accept = TRUE;
    }

    return starter;
}

State *transit(State *starter, State *state, Statement *input, Queue *queue) {
    State *result = NULL;
    TransitNode *transitNode = NULL;

    if (state == NULL || input == NULL)
        return NULL;
    switch (input->type) {
        case SYM:
            transitNode = get_transit_node_from_table(starter, state, input);
            if (transitNode != NULL) {
                result = transitNode->next;
            }
            break;

        case ARG:
            transitNode = get_transit_node_from_table(starter, state, input);
            if (transitNode != NULL) {
                add_key_value(queue, get_arg_name(transitNode->input), input);
                result = transitNode->next;
            }
            break;

        default:
            fprintf(stderr, "transit error!\n");
            break;
    }

    return result;
}


HashTable getStateTable() {
    return stateTable;
}

TransitNode *get_transit_node_from_table(State *starter, State *state, Statement *input) {
    TransitNode *transitNode = NULL;
    HashNode *node = get_table_element(stateTable, state, hash_code_state);
    while (node != NULL) {
        transitNode = (TransitNode *) node->element;
        if (equals_state(starter, transitNode->starter) && match_statement(input, transitNode->input)) {
            return transitNode;
        }
        node = node->next;
    }
    return NULL;
}

State *test_match(Mapping *mapping, Statement *source, Queue *replaceQueue) {
    State *start = mapping->starter;
    State *next = NULL;
    QueueNode *node = NULL;
    Statement *s = NULL;

    if (source->children != NULL) {
        node = source->children->head;
        while (node != NULL) {
            s = (Statement *) node->element;
            next = transit(mapping->starter, start, s, replaceQueue);
            start = next;
            if (start == NULL) {
                break;
            }
            node = node->next;
        }
    }

    return start;
}

